This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

source("tianfengRwrappers.R")

umapplot

umapplot(ds2)
ds2 <- RenameIdents(ds2,"FB" = "Fibroblast", "pericyte" = "Pericyte","fibromyocyte"="Fibromyocyte")
umapplot(ds2, group.by = "seurat_clusters")
Idents(ds2) <- factor(Idents(ds2),levels =
                        c("SMC1","Fibromyocyte","Pericyte","Fibroblast","SMC2"))
umapplot(ds2)
ds2$Classification1 <- Idents(ds2)
saveRDS(ds2,"ds2.rds")

ggsave("./fig3/ds2.svg",plot = umapplot(ds2),device = svg, width = 6, height = 5)
ds0 <- readRDS("ds0.rds")

周细胞的存在

multi_featureplot(c("FABP4", "RERGL", "NRIP2","HIGD1B"),ds2,labels = "")

基因表达 小提琴图

ds2 -> ds1

无监督聚类 harmony/CCA

viotheme <- theme(plot.title = element_text(size = 17,color="black",hjust = 0.5),
                 axis.title = element_text(size = 17,color ="black"), 
                 axis.text = element_text(size = 17,color = "black"),
                 panel.grid.minor.y = element_blank(),
                 panel.grid.minor.x = element_blank(),
                 axis.text.x = element_text(size= 17, angle = 0),
                 panel.grid=element_blank(),
                 legend.position = "top",
                 legend.text = element_text(size= 17),
                 legend.title= element_text(size= 17))

stat_theme <- stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test", size = 6,
                     label.y = max(merge_expr$expr),
                      hide.ns = F)

监督聚类 xgboost

ds2FbM <- subset(ds2,Classification1 == "Fibromyocyte")
ds1FbM <- subset(ds1,ref_celltype == "Fibromyocyte")

ds2data <- get_data_table(ds2FbM,type = "data")
ds1data <- get_data_table(ds1FbM,type = "data")


func1 <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}
merge_expr <- data.frame()
for (i in lapply(genes_to_show, func1,"ds1",ds1data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func1,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}
rownames(merge_expr) <- NULL
Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
# head(Data_summary)

ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("gene expression"),x=NULL, title = "Split violin") + 
  theme_classic()+ viotheme + stat_theme
ggobj
ggsave("./fig3/supds2tods1.png", device = png, plot = ggobj, height = 5, width = 7)

ds2 -> ds0

无监督聚类 harmony & CCA

监督聚类 xgboost

# Idents(ds0) <- ds0$ref_celltype
# umapplot(ds0,group.by = "ref_celltype")
ds2FbM <- subset(ds2, Classification1 == "Fibromyocyte")
ds0FbM <- subset(ds0, ref_celltype == "Fibromyocyte")

ds2data <- get_data_table(ds2FbM,type = "data")
ds0data <- get_data_table(ds0FbM,type = "data")

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func1,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func1,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)

ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("gene expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ viotheme + stat_theme
ggobj
ggsave("./fig3/supds2tods0.png", device = png, plot = ggobj, height = 5, width = 7)

viotheme <- theme(plot.title = element_text(size = 12,color="black",hjust = 0.5),
                     axis.title = element_text(size = 12,color ="black"), 
                     axis.text = element_text(size= 12,color = "black"),
                     panel.grid.minor.y = element_blank(),
                     panel.grid.minor.x = element_blank(),
                     axis.text.x = element_text(angle = 45, hjust = 1 ),
                     panel.grid=element_blank(),
                     legend.position = "top",
                     legend.text = element_text(size= 12),
                     legend.title= element_text(size= 12)) 

# https://stackoverflow.com/a/45614547
GeomSplitViolin <- ggproto("GeomSplitViolin", GeomViolin, draw_group = function(self, data, ..., draw_quantiles = NULL){
  data <- transform(data, xminv = x - violinwidth * (x - xmin), xmaxv = x + violinwidth * (xmax - x))
  grp <- data[1,'group']
  newdata <- plyr::arrange(transform(data, x = if(grp%%2==1) xminv else xmaxv), if(grp%%2==1) y else -y)
  newdata <- rbind(newdata[1, ], newdata, newdata[nrow(newdata), ], newdata[1, ])
  newdata[c(1,nrow(newdata)-1,nrow(newdata)), 'x'] <- round(newdata[1, 'x']) 
  if (length(draw_quantiles) > 0 & !scales::zero_range(range(data$y))) {
    stopifnot(all(draw_quantiles >= 0), all(draw_quantiles <= 
                                              1))
    quantiles <- ggplot2:::create_quantile_segment_frame(data, draw_quantiles)
    aesthetics <- data[rep(1, nrow(quantiles)), setdiff(names(data), c("x", "y")), drop = FALSE]
    aesthetics$alpha <- rep(1, nrow(quantiles))
    both <- cbind(quantiles, aesthetics)
    quantile_grob <- GeomPath$draw_panel(both, ...)
    ggplot2:::ggname("geom_split_violin", grid::grobTree(GeomPolygon$draw_panel(newdata, ...), quantile_grob))
  }
  else {
    ggplot2:::ggname("geom_split_violin", GeomPolygon$draw_panel(newdata, ...))
  }
})

geom_split_violin <- function (mapping = NULL, data = NULL, stat = "ydensity", position = "identity", ..., draw_quantiles = NULL, trim = TRUE, scale = "area", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(data = data, mapping = mapping, stat = stat, geom = GeomSplitViolin, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(trim = trim, scale = scale, draw_quantiles = draw_quantiles, na.rm = na.rm, ...))
}

scatter 比较

scatter_theme2 <- 
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))

ds2 -> ds0

ref_ds2FBM <- subset(ds2,Classification1 == "Fibromyocyte")

unaligned_ds0FBM <- subset(ds0,Classification1 == "Fibromyocyte")
harmony_ds0FbM <- subset(CAD_merge_harmony, orig.ident == "ds0" & ds2_celltype == "Fibromyocyte") #harmony
CCA_ds0FbM <- subset(CAD_merge_CCA, orig.ident == "ds0" & ds2_celltype == "Fibromyocyte")

xgb_ds0FbM <- subset(ds0, ref_celltype == "Fibromyocyte")

fig.3 harmony vs xgb vs ref

ggplot(data, aes(x=LUM, y=BGN, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=LUM, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=LUM, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=TAGLN, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

supp

unaligned vs CCA vs ref vs xgb

data1 <- FetchData(object = xgb_ds0FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data1) <-  NULL
data1$group <- "xgb_ds0"

data2 <- FetchData(object = CCA_ds0FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data2) <-  NULL
data2$group <- "CCA_ds0"

data3 <- FetchData(object = unaligned_ds0FBM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data3) <-  NULL
data3$group <- "unaligned_ds0"

data4 <- FetchData(object = ref_ds2FBM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data4) <-  NULL
data4$group <- "ref_ds2"



data <- rbind(data1,data2,data3,data4)

ggplot(data, aes(x=LUM, y=BGN, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=LUM, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=TAGLN, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

scatter

ds2 -> ds1

ref_ds2FBM <- subset(ds2,Classification1 == "Fibromyocyte")

unaligned_ds1FBM <- subset(ds1,Classification1 == "Fibromyocyte")
harmony_ds1FbM <- subset(CAD_merge_harmony, orig.ident == "ds1" & ds2_celltype == "Fibromyocyte") #harmony
CCA_ds1FbM <- subset(CAD_merge_CCA, orig.ident == "ds1" & ds2_celltype == "Fibromyocyte")

xgb_ds1FbM <- subset(ds1, ref_celltype == "Fibromyocyte")

fig.3 harmony vs xgb vs ref

data1 <- FetchData(object = xgb_ds1FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data1) <-  NULL
data1$group <- "xgb_ds1"

data2 <- FetchData(object = harmony_ds1FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data2) <-  NULL
data2$group <- "Harmony_ds1"

data3 <- FetchData(object = ref_ds2FBM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data3) <-  NULL
data3$group <- "ref_ds2"

data <- rbind(data1,data2,data3)

ggplot(data, aes(x=LUM, y=BGN, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=LUM, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=TAGLN, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

supp

unaligned vs CCA vs ref vs xgb

data1 <- FetchData(object = xgb_ds1FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data1) <-  NULL
data1$group <- "xgb_ds1"

data2 <- FetchData(object = CCA_ds1FbM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data2) <-  NULL
data2$group <- "CCA_ds1"

data3 <- FetchData(object = unaligned_ds1FBM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data3) <-  NULL
data3$group <- "unaligned_ds1"

data4 <- FetchData(object = ref_ds2FBM, vars = c("LUM", "ACTA2","BGN","TAGLN"))
rownames(data4) <-  NULL
data4$group <- "ref_ds2"



data <- rbind(data1,data2,data3,data4)

ggplot(data, aes(x=LUM, y=BGN, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=LUM, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

ggplot(data, aes(x=TAGLN, y=ACTA2, color = group, group = group)) +
  geom_point(size = 3,alpha = 0.1) + 
  geom_smooth(method=lm , se=TRUE) +
  theme_classic() +  theme(axis.title = element_text(size = 20,color = "black"),
        axis.text = element_text(size = 20,color = "black"),
        axis.line = element_line(size = 1),
        axis.ticks = element_line(size = 1),
        title = element_text(size = 20))
`geom_smooth()` using formula 'y ~ x'

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnNvdXJjZSgidGlhbmZlbmdSd3JhcHBlcnMuUiIpCmBgYAoKIyB1bWFwcGxvdApgYGB7cn0KdW1hcHBsb3QoZHMyKQpkczIgPC0gUmVuYW1lSWRlbnRzKGRzMiwiRkIiID0gIkZpYnJvYmxhc3QiLCAicGVyaWN5dGUiID0gIlBlcmljeXRlIiwiZmlicm9teW9jeXRlIj0iRmlicm9teW9jeXRlIikKdW1hcHBsb3QoZHMyLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiKQpJZGVudHMoZHMyKSA8LSBmYWN0b3IoSWRlbnRzKGRzMiksbGV2ZWxzID0KICAgICAgICAgICAgICAgICAgICAgICAgYygiU01DMSIsIkZpYnJvbXlvY3l0ZSIsIlBlcmljeXRlIiwiRmlicm9ibGFzdCIsIlNNQzIiKSkKdW1hcHBsb3QoZHMyKQpkczIkQ2xhc3NpZmljYXRpb24xIDwtIElkZW50cyhkczIpCnNhdmVSRFMoZHMyLCJkczIucmRzIikKCmdnc2F2ZSgiLi9maWczL2RzMi5zdmciLHBsb3QgPSB1bWFwcGxvdChkczIpLGRldmljZSA9IHN2Zywgd2lkdGggPSA2LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpkczAgPC0gcmVhZFJEUygiZHMwLnJkcyIpCmBgYAoKIyDlkajnu4bog57nmoTlrZjlnKgKYGBge3IgZmlnLndpZHRoPTYsZmlnLmhlaWdodD02fQptdWx0aV9mZWF0dXJlcGxvdChjKCJGQUJQNCIsICJSRVJHTCIsICJOUklQMiIsIkhJR0QxQiIpLGRzMixsYWJlbHMgPSAiIikKYGBgCgojIOWfuuWboOihqOi+viDlsI/mj5DnkLTlm74KIyMgZHMyIC0+IGRzMQojIyMg5peg55uR552j6IGa57G7IGhhcm1vbnkvQ0NBCmBgYHtyfQp2aW90aGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNyxjb2xvcj0iYmxhY2siLGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNyxjb2xvciA9ImJsYWNrIiksIAogICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTcsY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9IDE3LCBhbmdsZSA9IDApLAogICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQ9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9IDE3KSwKICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGU9IGVsZW1lbnRfdGV4dChzaXplPSAxNykpCgpzdGF0X3RoZW1lIDwtIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsCiAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsIHNpemUgPSA2LAogICAgICAgICAgICAgICAgICAgICBsYWJlbC55ID0gbWF4KG1lcmdlX2V4cHIkZXhwciksCiAgICAgICAgICAgICAgICAgICAgICBoaWRlLm5zID0gRikKYGBgCgpgYGB7cn0KZ2VuZXNfdG9fc2hvdyA8LSAgYygiRENOIiwiTFVNIiwiRkJMTjEiLCJNWUgxMSIpCgpkczJGYk0gPC0gc3Vic2V0KGRzMiwgQ2xhc3NpZmljYXRpb24xID09ICJGaWJyb215b2N5dGUiKQpkczJkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMkZiTSx0eXBlID0gImRhdGEiKQoKIyBkczFGYk0gPC0gc3Vic2V0KGRzMSwgQ2xhc3NpZmljYXRpb24xID09ICJGaWJyb215b2N5dGUiKQojIGRzMUZiTSA8LSBzdWJzZXQoQ0FEX21lcmdlX2hhcm1vbnksIG9yaWcuaWRlbnQgPT0gImRzMSIgJiBkczJfY2VsbHR5cGUgPT0gIkZpYnJvbXlvY3l0ZSIpCmRzMUZiTSA8LSBzdWJzZXQoQ0FEX21lcmdlX0NDQSwgb3JpZy5pZGVudCA9PSAiZHMxIiAmIGRzMl9jZWxsdHlwZSA9PSAiRmlicm9teW9jeXRlIikKCiMgZHMxZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczFGYk0sdHlwZSA9ICJkYXRhIikgI2hhcm1vbnkKZHMxZGF0YSA8LSBkczFGYk1AYXNzYXlzW1siU0NUIl1dQGRhdGEgJT4lIGFzLm1hdHJpeCgpI0NDQQoKCmZ1bmMxIDwtIGZ1bmN0aW9uKGdlbmUsIHNhbXBsZSwgZGF0YWJsZSl7CiAgZGF0YS5mcmFtZShleHByID0gZGF0YWJsZVtnZW5lLF0sIHNhbXBsZSA9IHNhbXBsZSwgZ2VuZSA9IGdlbmUpCn0KCm1lcmdlX2V4cHIgPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczEiLGRzMWRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMyIixkczJkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9Cgpyb3duYW1lcyhtZXJnZV9leHByKSA8LSBOVUxMCgpEYXRhX3N1bW1hcnkgPC0gUm1pc2M6OnN1bW1hcnlTRShtZXJnZV9leHByLCBtZWFzdXJldmFyPSJleHByIiwgZ3JvdXB2YXJzPWMoInNhbXBsZSIsImdlbmUiKSkKIyBoZWFkKERhdGFfc3VtbWFyeSkKCmdnb2JqIDwtIGdncGxvdChtZXJnZV9leHByLGFlcyh4ID0gZ2VuZSwgeSA9IGV4cHIsZmlsbCA9IHNhbXBsZSkpICsKICBnZW9tX3NwbGl0X3Zpb2xpbih0cmltPSBGLCBjb2xvcj0id2hpdGUiLCBzY2FsZSA9ICJhcmVhIikgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBEYXRhX3N1bW1hcnksYWVzKHggPSBnZW5lLCB5PSBleHByKSwgcGNoPTE5LAogICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4yKSxzaXplPSAxKSArICPnu5jliLblnYflgLzkvY3nva4KICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBEYXRhX3N1bW1hcnksIGFlcyh5bWluID0gZXhwci1jaSwgeW1heD0gZXhwcitjaSksIAogICAgICAgICAgICAgICAgd2lkdGg9IDAuMDUsIAogICAgICAgICAgICAgICAgcG9zaXRpb249IHBvc2l0aW9uX2RvZGdlKDAuMiksICPor6/lt67nur/kvY3nva7vvIzlkozlnYflgLzkvY3nva7nm7jljLnphY0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNywKICAgICAgICAgICAgICAgIHNpemU9IDAuNSkgKyBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5mb3JtYXQiLAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAid2lsY294LnRlc3QiLCBzaXplID0gNiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwueSA9IG1heChtZXJnZV9leHByJGV4cHIpLAogICAgICAgICAgICAgICAgICAgICAgaGlkZS5ucyA9IEYpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2IxZDZmYiIsICIjZmQ5OTk5IikpICsKICBsYWJzKHk9KCJnZW5lIGV4cHJlc3Npb24iKSx4PU5VTEwsdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIHZpb3RoZW1lICsgc3RhdF90aGVtZQpnZ29iagojIGdnc2F2ZSgiLi9maWczL3VuZHMydG9kczEucG5nIiwgZGV2aWNlID0gcG5nLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDUsIHdpZHRoID0gNykKZ2dzYXZlKCIuL2ZpZzMvQ0NBZHMydG9kczEucG5nIiwgZGV2aWNlID0gcG5nLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDUsIHdpZHRoID0gNykKYGBgCgojIyMg55uR552j6IGa57G7IHhnYm9vc3QgCmBgYHtyfQpkczJGYk0gPC0gc3Vic2V0KGRzMixDbGFzc2lmaWNhdGlvbjEgPT0gIkZpYnJvbXlvY3l0ZSIpCmRzMUZiTSA8LSBzdWJzZXQoZHMxLHJlZl9jZWxsdHlwZSA9PSAiRmlicm9teW9jeXRlIikKCmRzMmRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyRmJNLHR5cGUgPSAiZGF0YSIpCmRzMWRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMxRmJNLHR5cGUgPSAiZGF0YSIpCgoKZnVuYzEgPC0gZnVuY3Rpb24oZ2VuZSwgc2FtcGxlLCBkYXRhYmxlKXsKICBkYXRhLmZyYW1lKGV4cHIgPSBkYXRhYmxlW2dlbmUsXSwgc2FtcGxlID0gc2FtcGxlLCBnZW5lID0gZ2VuZSkKfQptZXJnZV9leHByIDwtIGRhdGEuZnJhbWUoKQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczEiLGRzMWRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMyIixkczJkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9CnJvd25hbWVzKG1lcmdlX2V4cHIpIDwtIE5VTEwKRGF0YV9zdW1tYXJ5IDwtIFJtaXNjOjpzdW1tYXJ5U0UobWVyZ2VfZXhwciwgbWVhc3VyZXZhcj0iZXhwciIsIGdyb3VwdmFycz1jKCJzYW1wbGUiLCJnZW5lIikpCiMgaGVhZChEYXRhX3N1bW1hcnkpCgpnZ29iaiA8LSBnZ3Bsb3QobWVyZ2VfZXhwcixhZXMoeCA9IGdlbmUsIHkgPSBleHByLGZpbGwgPSBzYW1wbGUpKSArCiAgZ2VvbV9zcGxpdF92aW9saW4odHJpbT0gRiwgY29sb3I9IndoaXRlIiwgc2NhbGUgPSAiYXJlYSIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gRGF0YV9zdW1tYXJ5LGFlcyh4ID0gZ2VuZSwgeT0gZXhwciksIHBjaD0xOSwKICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMiksc2l6ZT0gMSkgKyAj57uY5Yi25Z2H5YC85L2N572uCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gRGF0YV9zdW1tYXJ5LCBhZXMoeW1pbiA9IGV4cHItY2ksIHltYXg9IGV4cHIrY2kpLCAKICAgICAgICAgICAgICAgIHdpZHRoPSAwLjA1LCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uPSBwb3NpdGlvbl9kb2RnZSgwLjIpLCAj6K+v5beu57q/5L2N572u77yM5ZKM5Z2H5YC85L2N572u55u45Yy56YWNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsCiAgICAgICAgICAgICAgICBzaXplPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjFkNmZiIiwgIiNmZDk5OTkiKSkrIAogIGxhYnMoeT0oImdlbmUgZXhwcmVzc2lvbiIpLHg9TlVMTCwgdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIHZpb3RoZW1lICsgc3RhdF90aGVtZQpnZ29iagpnZ3NhdmUoIi4vZmlnMy9zdXBkczJ0b2RzMS5wbmciLCBkZXZpY2UgPSBwbmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNSwgd2lkdGggPSA3KQpgYGAKCiMjIGRzMiAtPiBkczAKIyMjIOaXoOebkeedo+iBmuexuyBoYXJtb255ICYgQ0NBCmBgYHtyfQpkczJGYk0gPC0gc3Vic2V0KGRzMixDbGFzc2lmaWNhdGlvbjEgPT0gIkZpYnJvbXlvY3l0ZSIpCmRzMmRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyRmJNLHR5cGUgPSAiZGF0YSIpCgojIGRzMEZiTSA8LSBzdWJzZXQoZHMwLENsYXNzaWZpY2F0aW9uMSA9PSAiRmlicm9teW9jeXRlIikKIyBkczBGYk0gPC0gc3Vic2V0KENBRF9tZXJnZV9oYXJtb255LCBvcmlnLmlkZW50ID09ICJkczAiICYgZHMyX2NlbGx0eXBlID09ICJGaWJyb215b2N5dGUiKSAjaGFybW9ueQoKZHMwRmJNIDwtIHN1YnNldChDQURfbWVyZ2VfQ0NBLCBvcmlnLmlkZW50ID09ICJkczAiICYgZHMyX2NlbGx0eXBlID09ICJGaWJyb215b2N5dGUiKQpkczBkYXRhIDwtIGRzMEZiTUBhc3NheXNbWyJTQ1QiXV1AZGF0YSAlPiUgYXMubWF0cml4KCkjIENDQQojIGRzMGRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwRmJNLHR5cGUgPSAiZGF0YSIpICMgSGFybW9ueQoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMCIsZHMwZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczIiLGRzMmRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KCnJvd25hbWVzKG1lcmdlX2V4cHIpIDwtIE5VTEwKCkRhdGFfc3VtbWFyeSA8LSBSbWlzYzo6c3VtbWFyeVNFKG1lcmdlX2V4cHIsIG1lYXN1cmV2YXI9ImV4cHIiLCBncm91cHZhcnM9Yygic2FtcGxlIiwiZ2VuZSIpKQojIGhlYWQoRGF0YV9zdW1tYXJ5KQoKZ2dvYmogPC0gZ2dwbG90KG1lcmdlX2V4cHIsYWVzKHggPSBnZW5lLCB5ID0gZXhwcixmaWxsID0gc2FtcGxlKSkgKwogIGdlb21fc3BsaXRfdmlvbGluKHRyaW09IEYsIGNvbG9yPSJ3aGl0ZSIsIHNjYWxlID0gImFyZWEiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IERhdGFfc3VtbWFyeSxhZXMoeCA9IGdlbmUsIHk9IGV4cHIpLCBwY2g9MTksCiAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjIpLHNpemU9IDEpICsgI+e7mOWItuWdh+WAvOS9jee9rgogIGdlb21fZXJyb3JiYXIoZGF0YSA9IERhdGFfc3VtbWFyeSwgYWVzKHltaW4gPSBleHByLWNpLCB5bWF4PSBleHByK2NpKSwgCiAgICAgICAgICAgICAgICB3aWR0aD0gMC4wNSwgCiAgICAgICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fZG9kZ2UoMC4yKSwgI+ivr+W3rue6v+S9jee9ru+8jOWSjOWdh+WAvOS9jee9ruebuOWMuemFjQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgIGFscGhhID0gMC43LAogICAgICAgICAgICAgICAgc2l6ZT0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2IxZDZmYiIsICIjZmQ5OTk5IikpKyAKICBsYWJzKHk9KCJnZW5lIGV4cHJlc3Npb24iKSx4PU5VTEwsdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIHZpb3RoZW1lICsgc3RhdF90aGVtZQpnZ29iagojIGdnc2F2ZSgiLi9maWczL3VuZHMydG9kczAucG5nIiwgZGV2aWNlID0gcG5nLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDUsIHdpZHRoID0gNykKZ2dzYXZlKCIuL2ZpZzMvQ0NBZHMydG9kczAucG5nIiwgZGV2aWNlID0gcG5nLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDUsIHdpZHRoID0gNykKYGBgCgojIyDnm5HnnaPogZrnsbsgeGdib29zdApgYGB7cn0KIyBJZGVudHMoZHMwKSA8LSBkczAkcmVmX2NlbGx0eXBlCiMgdW1hcHBsb3QoZHMwLGdyb3VwLmJ5ID0gInJlZl9jZWxsdHlwZSIpCmRzMkZiTSA8LSBzdWJzZXQoZHMyLCBDbGFzc2lmaWNhdGlvbjEgPT0gIkZpYnJvbXlvY3l0ZSIpCmRzMEZiTSA8LSBzdWJzZXQoZHMwLCByZWZfY2VsbHR5cGUgPT0gIkZpYnJvbXlvY3l0ZSIpCgpkczJkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMkZiTSx0eXBlID0gImRhdGEiKQpkczBkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMEZiTSx0eXBlID0gImRhdGEiKQoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMCIsZHMwZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczIiLGRzMmRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KCnJvd25hbWVzKG1lcmdlX2V4cHIpIDwtIE5VTEwKCkRhdGFfc3VtbWFyeSA8LSBSbWlzYzo6c3VtbWFyeVNFKG1lcmdlX2V4cHIsIG1lYXN1cmV2YXI9ImV4cHIiLCBncm91cHZhcnM9Yygic2FtcGxlIiwiZ2VuZSIpKQpoZWFkKERhdGFfc3VtbWFyeSkKCmdnb2JqIDwtIGdncGxvdChtZXJnZV9leHByLGFlcyh4ID0gZ2VuZSwgeSA9IGV4cHIsZmlsbCA9IHNhbXBsZSkpICsKICBnZW9tX3NwbGl0X3Zpb2xpbih0cmltPSBGLCBjb2xvcj0id2hpdGUiLCBzY2FsZSA9ICJhcmVhIikgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBEYXRhX3N1bW1hcnksYWVzKHggPSBnZW5lLCB5PSBleHByKSwgcGNoPTE5LAogICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4yKSxzaXplPSAxKSArICPnu5jliLblnYflgLzkvY3nva4KICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBEYXRhX3N1bW1hcnksIGFlcyh5bWluID0gZXhwci1jaSwgeW1heD0gZXhwcitjaSksIAogICAgICAgICAgICAgICAgd2lkdGg9IDAuMDUsIAogICAgICAgICAgICAgICAgcG9zaXRpb249IHBvc2l0aW9uX2RvZGdlKDAuMiksICPor6/lt67nur/kvY3nva7vvIzlkozlnYflgLzkvY3nva7nm7jljLnphY0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNywKICAgICAgICAgICAgICAgIHNpemU9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNiMWQ2ZmIiLCAiI2ZkOTk5OSIpKSsgCiAgbGFicyh5PSgiZ2VuZSBleHByZXNzaW9uIikseD1OVUxMLHRpdGxlID0gIlNwbGl0IHZpb2xpbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpKyB2aW90aGVtZSArIHN0YXRfdGhlbWUKZ2dvYmoKZ2dzYXZlKCIuL2ZpZzMvc3VwZHMydG9kczAucG5nIiwgZGV2aWNlID0gcG5nLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDUsIHdpZHRoID0gNykKYGBgCgpgYGB7cn0KdmlvdGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsY29sb3I9ImJsYWNrIixoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLGNvbG9yID0iYmxhY2siKSwgCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPSAxMixjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxICksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQ9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0gMTIpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGU9IGVsZW1lbnRfdGV4dChzaXplPSAxMikpIAoKIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNDU2MTQ1NDcKR2VvbVNwbGl0VmlvbGluIDwtIGdncHJvdG8oIkdlb21TcGxpdFZpb2xpbiIsIEdlb21WaW9saW4sIGRyYXdfZ3JvdXAgPSBmdW5jdGlvbihzZWxmLCBkYXRhLCAuLi4sIGRyYXdfcXVhbnRpbGVzID0gTlVMTCl7CiAgZGF0YSA8LSB0cmFuc2Zvcm0oZGF0YSwgeG1pbnYgPSB4IC0gdmlvbGlud2lkdGggKiAoeCAtIHhtaW4pLCB4bWF4diA9IHggKyB2aW9saW53aWR0aCAqICh4bWF4IC0geCkpCiAgZ3JwIDwtIGRhdGFbMSwnZ3JvdXAnXQogIG5ld2RhdGEgPC0gcGx5cjo6YXJyYW5nZSh0cmFuc2Zvcm0oZGF0YSwgeCA9IGlmKGdycCUlMj09MSkgeG1pbnYgZWxzZSB4bWF4diksIGlmKGdycCUlMj09MSkgeSBlbHNlIC15KQogIG5ld2RhdGEgPC0gcmJpbmQobmV3ZGF0YVsxLCBdLCBuZXdkYXRhLCBuZXdkYXRhW25yb3cobmV3ZGF0YSksIF0sIG5ld2RhdGFbMSwgXSkKICBuZXdkYXRhW2MoMSxucm93KG5ld2RhdGEpLTEsbnJvdyhuZXdkYXRhKSksICd4J10gPC0gcm91bmQobmV3ZGF0YVsxLCAneCddKSAKICBpZiAobGVuZ3RoKGRyYXdfcXVhbnRpbGVzKSA+IDAgJiAhc2NhbGVzOjp6ZXJvX3JhbmdlKHJhbmdlKGRhdGEkeSkpKSB7CiAgICBzdG9waWZub3QoYWxsKGRyYXdfcXVhbnRpbGVzID49IDApLCBhbGwoZHJhd19xdWFudGlsZXMgPD0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxKSkKICAgIHF1YW50aWxlcyA8LSBnZ3Bsb3QyOjo6Y3JlYXRlX3F1YW50aWxlX3NlZ21lbnRfZnJhbWUoZGF0YSwgZHJhd19xdWFudGlsZXMpCiAgICBhZXN0aGV0aWNzIDwtIGRhdGFbcmVwKDEsIG5yb3cocXVhbnRpbGVzKSksIHNldGRpZmYobmFtZXMoZGF0YSksIGMoIngiLCAieSIpKSwgZHJvcCA9IEZBTFNFXQogICAgYWVzdGhldGljcyRhbHBoYSA8LSByZXAoMSwgbnJvdyhxdWFudGlsZXMpKQogICAgYm90aCA8LSBjYmluZChxdWFudGlsZXMsIGFlc3RoZXRpY3MpCiAgICBxdWFudGlsZV9ncm9iIDwtIEdlb21QYXRoJGRyYXdfcGFuZWwoYm90aCwgLi4uKQogICAgZ2dwbG90Mjo6OmdnbmFtZSgiZ2VvbV9zcGxpdF92aW9saW4iLCBncmlkOjpncm9iVHJlZShHZW9tUG9seWdvbiRkcmF3X3BhbmVsKG5ld2RhdGEsIC4uLiksIHF1YW50aWxlX2dyb2IpKQogIH0KICBlbHNlIHsKICAgIGdncGxvdDI6OjpnZ25hbWUoImdlb21fc3BsaXRfdmlvbGluIiwgR2VvbVBvbHlnb24kZHJhd19wYW5lbChuZXdkYXRhLCAuLi4pKQogIH0KfSkKCmdlb21fc3BsaXRfdmlvbGluIDwtIGZ1bmN0aW9uIChtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIHN0YXQgPSAieWRlbnNpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIC4uLiwgZHJhd19xdWFudGlsZXMgPSBOVUxMLCB0cmltID0gVFJVRSwgc2NhbGUgPSAiYXJlYSIsIG5hLnJtID0gRkFMU0UsIHNob3cubGVnZW5kID0gTkEsIGluaGVyaXQuYWVzID0gVFJVRSkgewogIGxheWVyKGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gbWFwcGluZywgc3RhdCA9IHN0YXQsIGdlb20gPSBHZW9tU3BsaXRWaW9saW4sIHBvc2l0aW9uID0gcG9zaXRpb24sIHNob3cubGVnZW5kID0gc2hvdy5sZWdlbmQsIGluaGVyaXQuYWVzID0gaW5oZXJpdC5hZXMsIHBhcmFtcyA9IGxpc3QodHJpbSA9IHRyaW0sIHNjYWxlID0gc2NhbGUsIGRyYXdfcXVhbnRpbGVzID0gZHJhd19xdWFudGlsZXMsIG5hLnJtID0gbmEucm0sIC4uLikpCn0KYGBgCgotLS0KCiMjIHNjYXR0ZXIg5q+U6L6DIAoKYGBge3J9CnNjYXR0ZXJfdGhlbWUyIDwtIAogIHRoZW1lX2NsYXNzaWMoKSArICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCxjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDEpLAogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDEpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCkpCmBgYAoKCiMjIyBkczIgLT4gZHMwCgpgYGB7cn0KcmVmX2RzMkZCTSA8LSBzdWJzZXQoZHMyLENsYXNzaWZpY2F0aW9uMSA9PSAiRmlicm9teW9jeXRlIikKCnVuYWxpZ25lZF9kczBGQk0gPC0gc3Vic2V0KGRzMCxDbGFzc2lmaWNhdGlvbjEgPT0gIkZpYnJvbXlvY3l0ZSIpCmhhcm1vbnlfZHMwRmJNIDwtIHN1YnNldChDQURfbWVyZ2VfaGFybW9ueSwgb3JpZy5pZGVudCA9PSAiZHMwIiAmIGRzMl9jZWxsdHlwZSA9PSAiRmlicm9teW9jeXRlIikgI2hhcm1vbnkKQ0NBX2RzMEZiTSA8LSBzdWJzZXQoQ0FEX21lcmdlX0NDQSwgb3JpZy5pZGVudCA9PSAiZHMwIiAmIGRzMl9jZWxsdHlwZSA9PSAiRmlicm9teW9jeXRlIikKCnhnYl9kczBGYk0gPC0gc3Vic2V0KGRzMCwgcmVmX2NlbGx0eXBlID09ICJGaWJyb215b2N5dGUiKQpgYGAKCiMjIyBmaWcuMyBoYXJtb255IHZzIHhnYiB2cyByZWYKYGBge3J9CmRhdGExIDwtIEZldGNoRGF0YShvYmplY3QgPSB4Z2JfZHMwRmJNLCB2YXJzID0gYygiTFVNIiwgIkFDVEEyIiwiQkdOIiwiVEFHTE4iKSkKcm93bmFtZXMoZGF0YTEpIDwtICBOVUxMCmRhdGExJGdyb3VwIDwtICJ4Z2JfZHMwIgoKZGF0YTIgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IGhhcm1vbnlfZHMwRmJNLCB2YXJzID0gYygiTFVNIiwgIkFDVEEyIiwiQkdOIiwiVEFHTE4iKSkKcm93bmFtZXMoZGF0YTIpIDwtICBOVUxMCmRhdGEyJGdyb3VwIDwtICJIYXJtb255X2RzMCIKCmRhdGEzIDwtIEZldGNoRGF0YShvYmplY3QgPSByZWZfZHMyRkJNLCB2YXJzID0gYygiTFVNIiwgIkFDVEEyIiwiQkdOIiwiVEFHTE4iKSkKcm93bmFtZXMoZGF0YTMpIDwtICBOVUxMCmRhdGEzJGdyb3VwIDwtICJyZWZfZHMyIgoKZGF0YSA8LSByYmluZChkYXRhMSxkYXRhMixkYXRhMykKCmdncGxvdChkYXRhLCBhZXMoeD1MVU0sIHk9QkdOLCBjb2xvciA9IGdyb3VwLCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsYWxwaGEgPSAwLjEpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtICwgc2U9VFJVRSkgKyBzY2F0dGVyX3RoZW1lMgpnZ3Bsb3QoZGF0YSwgYWVzKHg9TFVNLCB5PUFDVEEyLCBjb2xvciA9IGdyb3VwLCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsYWxwaGEgPSAwLjEpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtICwgc2U9VFJVRSkgKyBzY2F0dGVyX3RoZW1lMgoKZ2dwbG90KGRhdGEsIGFlcyh4PVRBR0xOLCB5PUFDVEEyLCBjb2xvciA9IGdyb3VwLCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsYWxwaGEgPSAwLjEpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtICwgc2U9VFJVRSkgKyBzY2F0dGVyX3RoZW1lMgpgYGAKCiMjIHN1cHAKIyMjIHVuYWxpZ25lZCB2cyBDQ0EgdnMgcmVmIHZzIHhnYgpgYGB7cn0KZGF0YTEgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHhnYl9kczBGYk0sIHZhcnMgPSBjKCJMVU0iLCAiQUNUQTIiLCJCR04iLCJUQUdMTiIpKQpyb3duYW1lcyhkYXRhMSkgPC0gIE5VTEwKZGF0YTEkZ3JvdXAgPC0gInhnYl9kczAiCgpkYXRhMiA8LSBGZXRjaERhdGEob2JqZWN0ID0gQ0NBX2RzMEZiTSwgdmFycyA9IGMoIkxVTSIsICJBQ1RBMiIsIkJHTiIsIlRBR0xOIikpCnJvd25hbWVzKGRhdGEyKSA8LSAgTlVMTApkYXRhMiRncm91cCA8LSAiQ0NBX2RzMCIKCmRhdGEzIDwtIEZldGNoRGF0YShvYmplY3QgPSB1bmFsaWduZWRfZHMwRkJNLCB2YXJzID0gYygiTFVNIiwgIkFDVEEyIiwiQkdOIiwiVEFHTE4iKSkKcm93bmFtZXMoZGF0YTMpIDwtICBOVUxMCmRhdGEzJGdyb3VwIDwtICJ1bmFsaWduZWRfZHMwIgoKZGF0YTQgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHJlZl9kczJGQk0sIHZhcnMgPSBjKCJMVU0iLCAiQUNUQTIiLCJCR04iLCJUQUdMTiIpKQpyb3duYW1lcyhkYXRhNCkgPC0gIE5VTEwKZGF0YTQkZ3JvdXAgPC0gInJlZl9kczIiCgoKCmRhdGEgPC0gcmJpbmQoZGF0YTEsZGF0YTIsZGF0YTMsZGF0YTQpCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9TFVNLCB5PUJHTiwgY29sb3IgPSBncm91cCwgZ3JvdXAgPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLGFscGhhID0gMC4xKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD1sbSAsIHNlPVRSVUUpICsgc2NhdHRlcl90aGVtZTIKCmdncGxvdChkYXRhLCBhZXMoeD1MVU0sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9VEFHTE4sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpgYGAKCgoKIyMgc2NhdHRlciAKIyMjIGRzMiAtPiBkczEKYGBge3J9CnJlZl9kczJGQk0gPC0gc3Vic2V0KGRzMixDbGFzc2lmaWNhdGlvbjEgPT0gIkZpYnJvbXlvY3l0ZSIpCgp1bmFsaWduZWRfZHMxRkJNIDwtIHN1YnNldChkczEsQ2xhc3NpZmljYXRpb24xID09ICJGaWJyb215b2N5dGUiKQpoYXJtb255X2RzMUZiTSA8LSBzdWJzZXQoQ0FEX21lcmdlX2hhcm1vbnksIG9yaWcuaWRlbnQgPT0gImRzMSIgJiBkczJfY2VsbHR5cGUgPT0gIkZpYnJvbXlvY3l0ZSIpICNoYXJtb255CkNDQV9kczFGYk0gPC0gc3Vic2V0KENBRF9tZXJnZV9DQ0EsIG9yaWcuaWRlbnQgPT0gImRzMSIgJiBkczJfY2VsbHR5cGUgPT0gIkZpYnJvbXlvY3l0ZSIpCgp4Z2JfZHMxRmJNIDwtIHN1YnNldChkczEsIHJlZl9jZWxsdHlwZSA9PSAiRmlicm9teW9jeXRlIikKYGBgCgojIyMgZmlnLjMgaGFybW9ueSB2cyB4Z2IgdnMgcmVmCmBgYHtyfQpkYXRhMSA8LSBGZXRjaERhdGEob2JqZWN0ID0geGdiX2RzMUZiTSwgdmFycyA9IGMoIkxVTSIsICJBQ1RBMiIsIkJHTiIsIlRBR0xOIikpCnJvd25hbWVzKGRhdGExKSA8LSAgTlVMTApkYXRhMSRncm91cCA8LSAieGdiX2RzMSIKCmRhdGEyIDwtIEZldGNoRGF0YShvYmplY3QgPSBoYXJtb255X2RzMUZiTSwgdmFycyA9IGMoIkxVTSIsICJBQ1RBMiIsIkJHTiIsIlRBR0xOIikpCnJvd25hbWVzKGRhdGEyKSA8LSAgTlVMTApkYXRhMiRncm91cCA8LSAiSGFybW9ueV9kczEiCgpkYXRhMyA8LSBGZXRjaERhdGEob2JqZWN0ID0gcmVmX2RzMkZCTSwgdmFycyA9IGMoIkxVTSIsICJBQ1RBMiIsIkJHTiIsIlRBR0xOIikpCnJvd25hbWVzKGRhdGEzKSA8LSAgTlVMTApkYXRhMyRncm91cCA8LSAicmVmX2RzMiIKCmRhdGEgPC0gcmJpbmQoZGF0YTEsZGF0YTIsZGF0YTMpCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9TFVNLCB5PUJHTiwgY29sb3IgPSBncm91cCwgZ3JvdXAgPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLGFscGhhID0gMC4xKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD1sbSAsIHNlPVRSVUUpICsgc2NhdHRlcl90aGVtZTIKCmdncGxvdChkYXRhLCBhZXMoeD1MVU0sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9VEFHTE4sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpgYGAKCiMjIHN1cHAKIyMjIHVuYWxpZ25lZCB2cyBDQ0EgdnMgcmVmIHZzIHhnYgpgYGB7cn0KZGF0YTEgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHhnYl9kczFGYk0sIHZhcnMgPSBjKCJMVU0iLCAiQUNUQTIiLCJCR04iLCJUQUdMTiIpKQpyb3duYW1lcyhkYXRhMSkgPC0gIE5VTEwKZGF0YTEkZ3JvdXAgPC0gInhnYl9kczEiCgpkYXRhMiA8LSBGZXRjaERhdGEob2JqZWN0ID0gQ0NBX2RzMUZiTSwgdmFycyA9IGMoIkxVTSIsICJBQ1RBMiIsIkJHTiIsIlRBR0xOIikpCnJvd25hbWVzKGRhdGEyKSA8LSAgTlVMTApkYXRhMiRncm91cCA8LSAiQ0NBX2RzMSIKCmRhdGEzIDwtIEZldGNoRGF0YShvYmplY3QgPSB1bmFsaWduZWRfZHMxRkJNLCB2YXJzID0gYygiTFVNIiwgIkFDVEEyIiwiQkdOIiwiVEFHTE4iKSkKcm93bmFtZXMoZGF0YTMpIDwtICBOVUxMCmRhdGEzJGdyb3VwIDwtICJ1bmFsaWduZWRfZHMxIgoKZGF0YTQgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHJlZl9kczJGQk0sIHZhcnMgPSBjKCJMVU0iLCAiQUNUQTIiLCJCR04iLCJUQUdMTiIpKQpyb3duYW1lcyhkYXRhNCkgPC0gIE5VTEwKZGF0YTQkZ3JvdXAgPC0gInJlZl9kczIiCgoKCmRhdGEgPC0gcmJpbmQoZGF0YTEsZGF0YTIsZGF0YTMsZGF0YTQpCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9TFVNLCB5PUJHTiwgY29sb3IgPSBncm91cCwgZ3JvdXAgPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLGFscGhhID0gMC4xKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD1sbSAsIHNlPVRSVUUpICsgc2NhdHRlcl90aGVtZTIKCmdncGxvdChkYXRhLCBhZXMoeD1MVU0sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpnZ3Bsb3QoZGF0YSwgYWVzKHg9VEFHTE4sIHk9QUNUQTIsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMyxhbHBoYSA9IDAuMSkgKyAKICBnZW9tX3Ntb290aChtZXRob2Q9bG0gLCBzZT1UUlVFKSArIHNjYXR0ZXJfdGhlbWUyCgpgYGAKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==